package com.cy.jdk8.stream.demo2;

import com.alibaba.fastjson.JSON;
import com.cy.CommonUser;
import com.cy.User;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import org.testng.collections.Lists;

import java.sql.SQLOutput;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * https://blog.csdn.net/mu_wind/article/details/109516995
 *
 * @author : yue.cao
 * @since : 2021-04-15
 */
public class StreamDemo2 {

	@Test(description = "学前教育")
	public void learnBefore() {
		// Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true，调用get()方法会返回该对象
		Integer value1 = null;
		Integer value2 = 10;
		// Optional.ofNullable - 允许传递为 null 参数
		Optional<Integer> a = Optional.ofNullable(value1);
		// Optional.of - 如果传递的参数是 null，抛出异常 NullPointerException
		Optional<Integer> b = Optional.of(value2);
		System.out.println("第一个参数值存在: " + a.isPresent());
		System.out.println("第二个参数值存在: " + b.isPresent());
		// Optional.orElse - 如果值存在，返回它，否则返回默认值
		Integer v1 = a.orElse(0);
		//Optional.get - 获取值，值需要存在
		Integer v2 = b.get();
		System.out.println(v1 + v2);
	}

	@Test(description = "创建流")
	public void create() {
		List<String> list = Arrays.asList("a", "b", "c");
		// 创建一个顺序流
		Stream<String> stream = list.stream();
		// 创建一个并行流
		Stream<String> parallelStream = list.parallelStream();

		// 用数组创建流
		int[] array = {1, 3, 5, 6, 8};
		IntStream intStream = Arrays.stream(array);

		Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);

		Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
		stream2.forEach(x -> System.out.print(x + " "));
		System.out.println();

		Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
		stream3.forEach(System.out::println);

		// 串行-->并行
		Optional<Integer> findFirst = stream1.parallel().filter(x -> x % 2 == 0).findFirst();
		System.out.println(findFirst.get());
	}

	public static List<Person> personList = new ArrayList<>();

	static {
		personList.add(new Person("AA", 8900, 10, "male", "New York"));
		personList.add(new Person("BB", 7000, 11, "male", "Washington"));
		personList.add(new Person("CC", 7800, 12, "female", "Washington"));
		personList.add(new Person("DD", 8200, 13, "female", "New York"));
		personList.add(new Person("EE", 9500, 15, "male", "New York"));
		personList.add(new Person("FF", 7900, 10, "female", "New York"));
	}

	@Test(description = "遍历")
	public void t1() {
		List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
		// 遍历输出符合条件的元素
		list.stream().filter(x -> x > 6).forEach(x -> System.out.print(x + " "));
		// 匹配第一个
		Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
		// 匹配任意（适用于并行流）
		Optional<Integer> findAny = list.parallelStream().filter(x -> x > 1).findAny();
		// 是否包含符合特定条件的元素
		boolean anyMatch = list.stream().anyMatch(x -> x < 6);
		System.out.println("匹配第一个值：" + findFirst.get());
		System.out.println("匹配任意一个值：" + findAny.get());
		System.out.println("是否存在大于6的值：" + anyMatch);

		// 筛选出Integer集合中大于7的元素，并打印出来
		List<Integer> lists = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
		Stream<Integer> stream = lists.stream();
		stream.filter(x -> x > 7).forEach(System.out::println);

		// 筛选员工中工资高于8000的人，并形成新的集合
		List<String> fiterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
				.collect(Collectors.toList());
		System.out.print("高于8000的员工姓名：" + fiterList);
	}

	@Test(description = "聚合")
	public void t2() {
		List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd11111", "weoujgsd");
		// Optional<String> max = list.stream().max(Comparator.comparing(String::length));
		Optional<String> max = list.stream().max((o1, o2) -> o1.length() - o2.length());
		System.out.println("最长的字符串：" + max.get());
		Optional<Person> max1 = personList.stream().max(Comparator.comparingInt(Person::getSalary));
		System.out.println(max1);


		List<Integer> list1 = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
		long count = list1.stream().filter(x -> x > 6).count();
		System.out.println("list中大于6的元素个数：" + count);
	}

	@Test(description = "map/flatMap")
	public void t3() {
		// 英文字符串数组的元素全部改为大写。整数数组每个元素+3
		String[] strArr = {"abcd", "bcdd", "defde", "fTr"};
		List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

		List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
		List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());

		System.out.println("每个元素大写：" + strList);
		System.out.println("每个元素+3：" + intListNew);

		List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
		List<String> listNew = list.stream().flatMap(s -> {
			// 将每个元素转换成一个stream
			String[] split = s.split(",");
			Stream<String> s2 = Arrays.stream(split);
			return s2;
		}).collect(Collectors.toList());

		System.out.println("处理前的集合：" + list);
		System.out.println("处理后的集合：" + listNew);
	}

	@Test(description = "reduce")
	public void t4() {
		List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
		// 求和方式1
		list.stream().reduce((x, y) -> x + y);
		// 求和方式2
		list.stream().reduce(Integer::sum);
		// 求和方式3
		list.stream().reduce(0, Integer::sum);
		// 求乘积
		list.stream().reduce((x, y) -> x * y);
		// 求最大值方式1
		list.stream().reduce((x, y) -> x > y ? x : y);
		// 求最大值写法2
		list.stream().reduce(1, Integer::max);

		// 求工资之和方式1：
		Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
		// 求工资之和方式2：
		Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
				(sum1, sum2) -> sum1 + sum2);
		// 求工资之和方式3：
		Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

		// 求最高工资方式1：
		Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
				Integer::max);
		// 求最高工资方式2：
		Integer maxSalary2 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),
				(max1, max2) -> max1 > max2 ? max1 : max2);

		System.out.println("工资之和：" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3);
		System.out.println("最高工资：" + maxSalary + "," + maxSalary2);
	}

	@Test(description = "collect")
	public void t5() {
		List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
		List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
		Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
		personList.add(new Person("Anni", 8200, 24, "female", "New York"));

		Map<String, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
				.collect(Collectors.toMap(Person::getName, p -> p));
		System.out.println("toList:" + listNew);
		System.out.println("toSet:" + set);
		System.out.println("toMap:" + map);
	}

	@Test(description = "统计")
	public void t6() {
		// 求总数
		Long count = personList.stream().collect(Collectors.counting());
		// 求平均工资
		Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
		// 求最高工资
		Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
		// 求工资之和
		Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
		// 一次性统计所有信息
		DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
		System.out.println(collect);
	}

	@Test(description = "分组(partitioningBy/groupingBy)")
	public void t7() {
		// 将员工按薪资是否高于8000分组
		Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
		// 将员工按性别分组
		Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
		// 将员工先按性别分组，再按地区分组
		Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
		System.out.println("员工按薪资是否大于8000分组情况：" + part);
		System.out.println("员工按性别分组情况：" + group);
		System.out.println("员工按性别、地区：" + group2);
	}

	@Test(description = "接合(joining)")
	public void t8() {
		String collect = Lists.newArrayList("A", "b", "c").stream().collect(Collectors.joining("_"));
		System.out.println(collect);
	}

	@Test(description = "归约(reducing)")
	public void t9() {
		// 每个员工减去起征点后的薪资之和（这个例子并不严谨，但一时没想到好的例子）
		Integer sum = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j - 5000)));
		System.out.println("员工扣税薪资总和：" + sum);

		// stream的reduce
		Optional<Integer> sum2 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
		System.out.println("员工薪资总和：" + sum2.get());
	}

	@Test(description = "排序")
	public void t10() {
		// 按工资升序排序（自然排序）
		List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
				.collect(Collectors.toList());
		// 按工资倒序排序
		List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
				.map(Person::getName).collect(Collectors.toList());
		// 先按工资再按年龄升序排序
		List<String> newList3 = personList.stream()
				.sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
				.collect(Collectors.toList());
		// 先按工资再按年龄自定义排序（降序）
		List<String> newList4 = personList.stream().sorted((p1, p2) -> {
			if (p1.getSalary() == p2.getSalary()) {
				return p2.getAge() - p1.getAge();
			} else {
				return p2.getSalary() - p1.getSalary();
			}
		}).map(Person::getName).collect(Collectors.toList());
	}

	@Test(description = "提取/组合")
	public void t11() {
		Stream<String> stream1 = Stream.of("a", "b", "c", "d");
		Stream<String> stream2 = Stream.of("d", "e", "f", "g");
		// concat:合并两个流 distinct：去重
		List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
		// limit：限制从流中获得前n个数据
		List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
		// skip：跳过前n个数据 [0, 1, 2, 3, 4]
		List<Integer> collect2 = Stream.iterate(0, x -> x + 1).skip(2).limit(5).collect(Collectors.toList());
		System.out.println(collect2);
	}


	@Test(description = "自定义去重")
	public void t12() {
		ArrayList<Person> people = new ArrayList<>(
				personList.stream().collect(Collectors.toMap(
						Person::getName,
						Function.identity(),
						(old_case, new_case) -> new_case
				)).values()
		);

		Person[] people1 = people.toArray(new Person[0]);

		TreeSet<Person> people2 = new TreeSet<>((o1, o2) -> {
			if (o1.getAge() == o2.getAge()) {
				return 0;
			} else {
				return 1;
			}
		});

		people2.addAll(personList);
		System.out.println(people2);
	}

	@Test
	public void xxx_0() {
		Map<String, Date> map = new HashMap<>();
		List<String> list = Lists.newArrayList("1", "2", "3");
		DateTime now = DateTime.now();
		System.out.println(now);
		map.put(JSON.toJSONString(list), now.toDate());
		Map<Date, List<String>> groupByTime = map.entrySet()
				.stream()
				.collect(
						Collectors.groupingBy(
								Map.Entry::getValue, // 精确到分钟
								Collectors.mapping(Map.Entry::getKey, Collectors.toList())
						)
				);
		System.out.println(groupByTime);
	}

	@Test
	public void xxx_1() {
		List<CommonUser> users = Arrays.asList(new CommonUser("ruben", 18), new CommonUser("vampire", 18));
		Map<Integer, List<String>> ageNamesMap = users.stream()
				.collect(Collectors.groupingBy(CommonUser::getAge, Collectors.mapping(CommonUser::getName, Collectors.toList())));
		// 第二个参数就是说你分组之后还想做一些后续处理，例如根据age分组后再根据name分组一次，或者我这里的，分组后，使用
		// 可以在分组后再获取其中属性，或者还可以进行别的计算如分组后再根据每一组内获取最大值等
		System.out.println(ageNamesMap);
	}

	@Test
	public void xxx_2() {


	}


}
